package hpp.sort;

/**
 * Created by hpp on 2017/8/4.
 */
public class SortTest {

    public static void main(String[] args) {

        int[] array = {1,2,5,4,7,9,0,8,3,6};
//        insertSort(array,array.length);
//        insertSort2(array,array.length);
//        shellSort(array);
//        bubbleSort(array);
//        quickSort(array, 0, array.length - 1);
//        selectSort(array);
//        heapSort(array);
        mergeSort(array);

        for(int item:array){
            System.out.print(item + " ");
        }

    }

    /**
     * 直接插入排序，空间复杂度O（1），时间复杂度O（n?）
     * 适用于基本有序的排序表和数据量不大的排序表
     */
    public static void insertSort(int[] array, int n){
        if(array==null)
            return;
        int i,j,temp;
        for(i=1;i<n;i++){
            if(array[i]<array[i-1]){//减少比较移动的次数
                temp = array[i];
                j = i - 1;
                while (j>=0){
                    if (temp<array[j]) {
                        array[j + 1] = array[j];
                    } else {
                        break;
                    }
                    j--;
                }
                array[j+1] = temp;
            }
        }
    }


    /**
     * 折半插入排序，空间复杂度O（1），时间复杂度O（n?）
     * @param array
     * @param n
     */
    public static void insertSort2(int[] array, int n){

        if(array==null)
            return;;
        int i,j,temp,low,high,mid;
        for(i=1;i<n;i++){
            temp = array[i];
            low = 0;
            high = i-1;
            while (low<=high){//找到要插入的位置
                mid = (low + high)/2;
                if(array[mid]>temp){
                    high = mid - 1;
                }else {
                    low = mid + 1;
                }
            }

            for(j = i-1;j>=high+1;--j){
                array[j+1] = array[j];
            }
            array[high+1] = temp;
        }
    }


    /**
     * 希尔排序
     * @param arr
     */
    private static void shellSort(int[] arr) {

        int j;
        int len = arr.length;
        for(int val=len>>1; val>0; val>>=1) {
            //下面是对本次的所有分组做直接插入排序
            for(int i=val; i<len; i++) {
                int temp = arr[i];
				/*
				 * 为什么每次都用temp比较呢？
				 * 因为直接插入就是找到temp的合适位置。
				 * 为什么temp<arr[j-val]这个条件可以放在for内呢？
				 * 因为原来的组内数据已经有序，找到位置就停止便是。
				 * 不甚理解的去看直接插入排序吧。
				 */
                for(j=i; j>=val&&temp<arr[j-val]; j-=val) {
					/*
					 * 为什么是arr[j-val]不是arr[j]呢？
					 * 因为j=i开始的，而且条件是j>=val&&temp<arr[j-val]
					 */
                    arr[j] = arr[j-val];
                }
				/*
				 * 注意不是arr[i] = temp
				 * 直接插入排序也是这样的。
				 * 为什么呢？
				 * 因为j是位置，i是待插入元素
				 */
                arr[j] = temp;
            }
        }
    }


    /**
     * 冒泡排序，空间复杂度O（1），时间复杂度O（n?），稳定
     * @param array
     */
    private static void bubbleSort(int[] array){
        if (array==null)
            return;
        int length = array.length-1;
        for(int i=0;i<length;i++){
            for (int j=length;j>i;j--){
                if (array[j-1]>array[j]){
                    int tmp = array[j];
                    array[j] = array[j-1];
                    array[j-1] = tmp;
                }
            }
        }
    }


    /**
     * 快排，空间复杂度最坏O（n）,平均O（logn）,时间复杂度最坏情况下O（n?），平均情况下O（nlogn），不稳定
     */
    public static void quickSort(int[] array,int left,int right){
        if (array==null)
            return;
        if (left<right){
            int pivot = partition(array,left,right);
            quickSort(array,left,pivot-1);
            quickSort(array,pivot+1,right);
        }
    }
    //快排一次划分
    private static int partition(int[] array,int left,int right){
        int tmp = array[left];
        while (left<right){
            while (left<right && array[right]>=tmp)// 从右向左找小于tmp的数来填array[left]
                right--;
            if (left<right)
                array[left++] = array[right];
            while (left<right && array[left]<tmp)// 从左向右找小于tmp的数来填array[right]
                left++;
            if (left<right)
                array[right--] = array[left];
        }
        array[left] = tmp;//此时的left相当于pivot
        return left;
    }


    /**
     * 简单选择排序,空间复杂度O(1),时间复杂度O（n?），不稳定
     */
    public static void selectSort(int[] array){
        if (array==null)
            return;
        int length = array.length;
        for(int i=0;i<length;i++){
            int min = i;//记录最小元素位置
            for(int j=i+1;j<length;j++){
                if(array[j]<array[min]){//更新最小元素位置
                    min = j;
                }
            }
            if (min!=i){//把最小元素和i位置上的元素交换
                int tmp = array[i];
                array[i] = array[min];
                array[min] = tmp;
            }
        }
    }


    /**
     * 堆排序：空间复杂度O（1），时间复杂度O（nlogn）,不稳定
     */
//    public static void buildMaxHeap(int[] array,int len){
//        for(int i=len/2;i>0;i--){
//            adjustDown(array,i,len);
//        }
//
//    }
//
//    private static void adjustDown(int[] array,int k,int len){
//        int tmp = array[k];
//        for(int i=2*k;i<len;i=k*2){
//            if (i<len && array[i]<array[i+1])
//                i++;
//            if (tmp>=array[i])
//                break;
//            else {
//                array[k] = array[i];
//                k = i;
//            }
//        }
//        array[k] = tmp;
//    }
//
//    public static void heapSort(int[] array,int len){
//        if (array==null)
//            return;
//        buildMaxHeap(array,len);
//        for(int i=len-1;i>0;i--){
//            int tmp = array[i];
//            array[i] = array[0];
//            array[0] = tmp;
//            adjustDown(array,0,i-1);
//        }
//    }

    public static void heapSort(int[] data) {

        buildMaxHeap(data);// 构建最大堆
        for (int i = data.length; i > 1; i--) {// 循环，每次把根节点和最后一个节点调换位置
            int tmp = data[0];
            data[0] = data[i - 1];
            data[i - 1] = tmp;
            maxHeapify(data, 1, i - 1);// 堆的长度减少1，排除置换到最后位置的根节点
        }
    }

    // 根据输入数组构建一个最大堆
    private static void buildMaxHeap(int[] array) {
        for (int i = array.length / 2; i > 0; i--) {
            maxHeapify(array, i, array.length);
        }
    }

    //堆调整，使其生成最大堆
    private static void maxHeapify(int[] data, int parentNodeIndex, int heapSize) {
        // 左子节点索引
        int leftChildNodeIndex = parentNodeIndex * 2;
        // 右子节点索引
        int rightChildNodeIndex = parentNodeIndex * 2 + 1;
        // 最大节点索引
        int largestNodeIndex = parentNodeIndex;

        // 如果左子节点大于父节点，则将左子节点作为最大节点
        if (leftChildNodeIndex <= heapSize && data[leftChildNodeIndex - 1]>data[parentNodeIndex - 1]) {
            largestNodeIndex = leftChildNodeIndex;
        }

        // 如果右子节点比最大节点还大，那么最大节点应该是右子节点
        if (rightChildNodeIndex <= heapSize && data[rightChildNodeIndex - 1]>data[largestNodeIndex - 1]) {
            largestNodeIndex = rightChildNodeIndex;
        }

        // 最后，如果最大节点和父节点不一致，则交换他们的值
        if (largestNodeIndex != parentNodeIndex) {
            int tmp = data[parentNodeIndex - 1];
            data[parentNodeIndex - 1] = data[largestNodeIndex - 1];
            data[largestNodeIndex - 1] = tmp;
            // 交换完父节点和子节点的值，对换了值的子节点检查是否符合最大堆的特性
            maxHeapify(data, largestNodeIndex, heapSize);
        }
    }


    /**
     * 2-路归并排序,空间复杂度O（n）,时间复杂度O(nlogn)
     */
    public static void mergeSort(int[] data) {
        sort(data, 0, data.length - 1);
    }

    public static void sort(int[] data, int left, int right) {
        if (left >= right)
            return;
        // 找出中间索引
        int center = (left + right) / 2;
        // 对左边数组进行递归
        sort(data, left, center);
        // 对右边数组进行递归
        sort(data, center + 1, right);
        // 合并
        merge(data, left, center, right);
    }

    /**
     * 将两个数组进行归并，归并前面2个数组已有序，归并后依然有序
     * @param data    数组对象
     * @param left    左数组的第一个元素的索引
     * @param center    左数组的最后一个元素的索引，center+1是右数组第一个元素的索引
     * @param right    右数组最后一个元素的索引
     */
    public static void merge(int[] data, int left, int center, int right) {
        // 临时数组
        int[] tmpArr = new int[data.length];
        // 右数组第一个元素索引
        int mid = center + 1;
        // third 记录临时数组的索引
        int third = left;
        // 缓存左数组第一个元素的索引
        int tmp = left;
        while (left <= center && mid <= right) {
            // 从两个数组中取出最小的放入临时数组
            if (data[left] <= data[mid]) {
                tmpArr[third++] = data[left++];
            } else {
                tmpArr[third++] = data[mid++];
            }
        }
        // 剩余部分依次放入临时数组（实际上两个while只会执行其中一个）
        while (mid <= right) {
            tmpArr[third++] = data[mid++];
        }
        while (left <= center) {
            tmpArr[third++] = data[left++];
        }
        // 将临时数组中的内容拷贝回原数组中
        // （原left-right范围的内容被复制回原数组）
        while (tmp <= right) {
            data[tmp] = tmpArr[tmp++];
        }
    }



}
